<?php

/**
 * Created by PhpStorm.
 * User: yf
 * Date: 2017/12/4
 * Time: 下午5:49
 */
namespace App\System\Route;

use App\System\Component\Invoker;
use App\System\Config\Config;
use App\System\Http\AbstractInterface\Router;
use App\System\Http\Request;
use App\System\Http\Response;
use App\System\Http\UrlParser;
use FastRoute\Dispatcher\GroupCountBased;

class Dispatcher {
	private $controllerNameSpacePrefix;
	private $router = null;
	private $routerMethodNotAllowCallBack = null;
	function __construct($controllerNameSpace) {
		$this->controllerNameSpacePrefix = trim ( $controllerNameSpace, '\\' );
	}
	public function dispatch(Request $request, Response $response): void {
		if ($this->router === null) {
			$router = $this->checkRouter ();
			if ($router) {
				$this->routerMethodNotAllowCallBack = $router->getMethodNotAllowCallBack ();
				$this->router = new GroupCountBased ( $router->getRouteCollector ()->getData () );
			} else {
				$this->router = false;
			}
		}
		
		$this->router ( $request, $response );
	}
	private function checkRouter(): ?Router {
		$class = $this->controllerNameSpacePrefix . '\\Router';
		if (class_exists ( $class )) {
			$router = new $class ();
			if ($router instanceof Router) {
				return $router;
			} else {
				return null;
			}
		} else {
			return null;
		}
	}
	private function router(Request $request, Response $response): void {
		if ($this->router instanceof GroupCountBased) {
			$config = Config::get ();
			$path = $request->getUri ()->getPath ();
			
			if (strpos ( $path, '/' . $config ['system'] ['entrance'] ) !== false) {
				$path = str_replace ( '/' . $config ['system'] ['entrance'], "", $path );
			}
			if (empty ( $path )) {
				$path = "/";
			}
			$routeInfo = $this->router->dispatch ( $request->getMethod (), UrlParser::pathInfo ( $path ) );
			
			if ($routeInfo !== false) {
				switch ($routeInfo [0]) {
					case \FastRoute\Dispatcher::NOT_FOUND :
						{
							$this->controllerHandler ( $request, $response );
							break;
						}
					case \FastRoute\Dispatcher::METHOD_NOT_ALLOWED :
						{
							if ($this->routerMethodNotAllowCallBack) {
								Invoker::callUserFunc ( $this->routerMethodNotAllowCallBack, $request, $response );
							} else {
								throw new \Exception ( "pathinfo:" . $path . " can't use " . $request->getServerParams ( "REQUEST_METHOD" ) . " method" );
							}
							break;
						}
					case \FastRoute\Dispatcher::FOUND :
						{
							$handler = $routeInfo [1];
							$vars = $routeInfo [2];
							
							// $handler = $this->controllerNameSpacePrefix.$handler;
							if (is_callable ( $handler )) {
								Invoker::callUserFuncArray ( $handler, array_merge ( [ 
										$request,
										$response 
								], array_values ( $vars ) ) );
							} else if (is_string ( $handler )) {
								$data = $request->getQueryParams ();
								$request->withQueryParams ( $vars + $data );
								$pathInfo = UrlParser::pathInfo ( $handler );
								$request->getUri ()->withPath ( $pathInfo );
								$this->controllerHandler ( $request, $response );
							}
							
							break;
						}
					default :
						{
							break;
						}
				}
			}
		}
	}
	private function controllerHandler(Request $request, Response $response) {
		$config = Config::get ();
		$path = $request->getUri ()->getPath ();
		
		if (strpos ( $path, '/' . $config ['system'] ['entrance'] ) !== false) {
			$path = str_replace ( '/' . $config ['system'] ['entrance'], "", $path );
		}
		if (empty ( $path )) {
			$path = "/";
		}
		$pathInfo = ltrim ( UrlParser::pathInfo ( $path ), "/" );
	
		$list = explode ( "/", $pathInfo );
		$actionName = null;
		$finalClass = null;
		$controlMaxDepth = $config ['system'] ['HTTP_CONTROLLER_MAX_DEPTH'];
		$currentDepth = count ( $list );
		$maxDepth = $currentDepth < $controlMaxDepth ? $currentDepth : $controlMaxDepth;
		while ( $maxDepth >= 0 ) {
			$className = '';
			for($i = 0; $i < $maxDepth; $i ++) {
				$className = $className . "\\" . ucfirst ( $list [$i] ?: 'Index' ); // 为一级控制器Index服务
				
			}
			if (class_exists ( $this->controllerNameSpacePrefix . $className )) {
				// 尝试获取该class后的actionName
				$actionName = empty ( $list [$i] ) ? 'index' : $list [$i];
				$finalClass = $this->controllerNameSpacePrefix . $className;
				break;
			} else {
				// 尝试搜搜index控制器
				$temp = $className . "\\Index";
				if (class_exists ( $this->controllerNameSpacePrefix . $temp )) {
					$finalClass = $this->controllerNameSpacePrefix . $temp;
					// 尝试获取该class后的actionName
					$actionName = empty ( $list [$i] ) ? 'index' : $list [$i];
					break;
				}
			}
			$maxDepth --;
		}
		if (! empty ( $finalClass )) {
			(new $finalClass ( $actionName, $request, $response ));
		} else {
			throw new \Exception ( "pathInfo:" . $pathInfo . "   not found!" );
		}
	}
}